package leetcode.editor.cn;
//现在总共有 numCourses 门课需要选，记为 0 到 numCourses-1。 
//
// 给定一个数组 prerequisites ，它的每一个元素 prerequisites[i] 表示两门课程之间的先修顺序。 例如 
//prerequisites[i] = [ai, bi] 表示想要学习课程 ai ，需要先完成课程 bi 。 
//
// 请根据给出的总课程数 numCourses 和表示先修顺序的 prerequisites 得出一个可行的修课序列。 
//
// 可能会有多个正确的顺序，只要任意返回一种就可以了。如果不可能完成所有课程，返回一个空数组。 
//
// 
//
// 示例 1: 
//
// 
//输入: numCourses = 2, prerequisites = [[1,0]] 
//输出: [0,1]
//解释: 总共有 2 门课程。要学习课程 1，你需要先完成课程 0。因此，正确的课程顺序为 [0,1] 。 
//
// 示例 2: 
//
// 
//输入: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
//输出: [0,1,2,3] or [0,2,1,3]
//解释: 总共有 4 门课程。要学习课程 3，你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
// 因此，一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。
// 
//
// 示例 3: 
//
// 
//输入: numCourses = 1, prerequisites = [] 
//输出: [0]
//解释: 总共 1 门课，直接修第一门课就可。 
//
// 
//
// 提示: 
//
// 
// 1 <= numCourses <= 2000 
// 0 <= prerequisites.length <= numCourses * (numCourses - 1) 
// prerequisites[i].length == 2 
// 0 <= ai, bi < numCourses 
// ai != bi 
// prerequisites 中不存在重复元素 
// 
//
// 
//
// 
// 注意：本题与主站 210 题相同：https://leetcode-cn.com/problems/course-schedule-ii/ 
//
// Related Topics 深度优先搜索 广度优先搜索 图 拓扑排序 👍 36 👎 0


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

//leetcode submit region begin(Prohibit modification and deletion)
class SolutionOffer2_113 {
    // 存储有向图
    List<List<Integer>> edges;
    // 标记每个节点的状态：0=未搜索，1=搜索中，2=已完成
    int[] visited;
    // 用数组来模拟栈，下标 n-1 为栈底，0 为栈顶
    int[] result;
    // 判断有向图中是否有环
    boolean valid = true;
    // 栈下标
    int index;
    public int[] findOrder1(int numCourses, int[][] prerequisites) {
        edges = new ArrayList<List<Integer>>();
        for(int i = 0; i < numCourses; i++){
            edges.add(new ArrayList<Integer>());
        }
        visited = new int[numCourses];
        result = new int[numCourses];
        index = numCourses - 1;
        for(int[] info : prerequisites){
            edges.get(info[1]).add(info[0]);
        }
        for(int i = 0; i < numCourses; i++){
            if(visited[i] == 0){
                dfs(i);
            }
        }
        return !valid ? new int[0] : result;
    }

    private void dfs(int i) {
        if(visited[i] == 2 || !valid){
            return;
        }
        visited[i] = 1;
        for(int next : edges.get(i)){
            if(visited[next] == 0){
                dfs(next);
                if(!valid) {
                    return;
                }
            }
            if(visited[next] == 1){
                valid = false;
                return;
            }
        }
        visited[i] = 2;
        result[index--] = i;
    }


    // 存储每个节点的入度
    int[] indeg;
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        visited = new int[numCourses];
        indeg = new int[numCourses];
        index = 0;
        edges = new ArrayList<>();
        result = new int[numCourses];

        for(int i = 0; i < numCourses; i++){
            edges.add(new ArrayList<Integer>());
        }
        for(int[] info : prerequisites) {
            edges.get(info[1]).add(info[0]);
            indeg[info[0]]++;
        }
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (indeg[i] == 0) {
                queue.offer(i);
            }
        }
        while (!queue.isEmpty()) {
            Integer zeroIn = queue.poll();
            result[index++] = zeroIn;
            for(int next : edges.get(zeroIn)){
                indeg[next]--;
                if(indeg[next] == 0){
                    queue.offer(next);
                }
            }
        }
        if(index < numCourses) {
            return new int[0];
        }
        return result;
    }



//    public static void main(String[] args) {
//        int[] order = new SolutionOffer2_113().findOrder(2, new int[][]{{0, 1}, {1, 0}});
//        System.out.println(order);
//    }
}
//leetcode submit region end(Prohibit modification and deletion)
